# -*- coding: utf-8 -*-
# @Time    : 2024/7/1 16:07
# @Author  : yujiahao
# @File    : 14_numpy_linear_algebra.py
# @description:NumPy线性代数


"""
NumPy 线性代数模块 `numpy.linalg` 常用函数简介：

1. numpy.dot()
- 功能: 计算两个数组的点积。对于一维数组，结果是两个向量的内积。对于二维数组，结果是矩阵乘法。对于更高维度，结果是最后一个轴和倒数第二个轴的和的乘积。

2. numpy.vdot()
- 功能: 计算两个向量的点积，所有元素展平后计算。与 `numpy.dot` 不同，`vdot` 会将输入数组展平后再进行点积计算。

3. numpy.inner()
- 功能: 计算两个数组的内积。对于一维数组，结果是两个向量的内积。对于更高维度，结果是最后一个轴的和的乘积。

4. numpy.matmul()
- 功能: 计算两个数组的矩阵积。与 `numpy.dot` 类似，但专门用于矩阵乘法。对于一维和二维数组，`matmul` 和 `dot` 的行为是一致的，但对于更高维度，`matmul` 进行的是矩阵乘法而非一般的乘积。

5. numpy.linalg.det()
- 功能: 计算矩阵的行列式。行列式是一个标量值，可以用于判断矩阵是否可逆。

6. numpy.linalg.solve()
- 功能: 求解线性矩阵方程或线性方程组。给定一个矩阵和一个向量，`solve` 可以找到使得矩阵与向量相乘等于给定向量的解。

7. numpy.linalg.inv()
- 功能: 计算矩阵的逆矩阵。逆矩阵是一个矩阵，使得原矩阵和逆矩阵相乘得到单位矩阵。

这些函数提供了强大的线性代数计算能力，适用于各种科学计算和工程应用。
"""

import numpy as np

# todo 1、计算两个数组的点积
# 对于一维数组，结果是两个向量的内积
# A = [1, 2, 3]
# B = [4, 5, 6]
# 计算过程：
# 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32
A = [1, 2, 3]
B = [4, 5, 6]
print(np.dot(A, B))  # 输出: 32

# todo 2、计算两个向量的点积，所有元素展平后计算
# a = np.array([[100, 200], [23, 12]])
# b = np.array([[10, 20], [12, 21]])
# 计算过程：
# 展平后 a = [100, 200, 23, 12]
# 展平后 b = [10, 20, 12, 21]
# 100*10 + 200*20 + 23*12 + 12*21 = 1000 + 4000 + 276 + 252 = 5528
a = np.array([[100, 200], [23, 12]])
b = np.array([[10, 20], [12, 21]])
print(np.vdot(a, b))  # 输出: 5528

# todo 3、计算两个数组的内积
# 对于一维数组，结果是两个向量的内积
# A = [[1, 10], [100, 1000]]
# B = [[1, 2], [3, 4]]
# 计算过程：
# 内积结果是一个矩阵，其中每个元素是两个向量的内积
# A[0] 和 B[0] 的内积: 1*1 + 10*2 = 1 + 20 = 21
# A[0] 和 B[1] 的内积: 1*3 + 10*4 = 3 + 40 = 43
# A[1] 和 B[0] 的内积: 100*1 + 1000*2 = 100 + 2000 = 2100
# A[1] 和 B[1] 的内积: 100*3 + 1000*4 = 300 + 4000 = 4300
A = [[1, 10], [100, 1000]]
B = [[1, 2], [3, 4]]
print(np.inner(A, B))
# 输出:
# [[  21   43]
#  [2100 4300]]

# todo 4、计算两个数组的矩阵积
# 专门用于矩阵乘法，对于一维和二维数组，matmul 和 dot 的行为是一致的
# 但对于更高维度，matmul 进行的是矩阵乘法而非一般的乘积
# a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# b = np.array([[23, 23, 12], [2, 1, 2], [7, 8, 9]])
# 计算过程：
# 结果矩阵的每个元素是对应行和列的点积
# C[0,0] = 1*23 + 2*2 + 3*7 = 23 + 4 + 21 = 48
# C[0,1] = 1*23 + 2*1 + 3*8 = 23 + 2 + 24 = 49
# C[0,2] = 1*12 + 2*2 + 3*9 = 12 + 4 + 27 = 43
# 依此类推计算其他元素
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = np.array([[23, 23, 12], [2, 1, 2], [7, 8, 9]])
print(np.matmul(a, b))
# 输出:
# [[ 48  49  43]
#  [144 145 112]
#  [240 241 181]]

# todo 5、计算矩阵的行列式
"""
计算矩阵的行列式
行列式是一个标量值，可以用于判断矩阵是否可逆。

对于 2x2 矩阵 A，其行列式的计算公式如下：
A = [[a, b],
     [c, d]]

行列式 det(A) 的计算公式为：
det(A) = a*d - b*c

示例计算：
给定矩阵 a:
a = [[1, 2],
     [3, 4]]

步骤 1: 确定矩阵元素
矩阵 a 的元素为：
a = 1, b = 2, c = 3, d = 4

步骤 2: 应用行列式公式
根据行列式公式：
det(a) = a*d - b*c

将矩阵 a 的元素代入公式：
det(a) = (1 * 4) - (2 * 3)

步骤 3: 计算结果
进行计算：
det(a) = 4 - 6 = -2

所以，矩阵 a 的行列式为 -2。

使用 NumPy 计算行列式，代码如下：
"""
a = np.array([[1, 2], [3, 4]])
print(np.linalg.det(a))  # 输出: -2.0000000000000004

# todo 6、求解线性矩阵方程或线性方程组
# 给定一个矩阵和一个向量，solve 可以找到使得矩阵与向量相乘等于给定向量的解
# m = np.array([[3, 2, 1], [1, 1, 1], [1, 2, -1]])
# n = np.array([[10], [6], [2]])
# 计算过程：
# 设 x = [x1, x2, x3]
# 则方程组为：
# 3x1 + 2x2 + x3 = 10
# x1 + x2 + x3 = 6
# x1 + 2x2 - x3 = 2
# 解得 x1 = 1, x2 = 2, x3 = 3
m = np.array([[3, 2, 1], [1, 1, 1], [1, 2, -1]])
n = np.array([[10], [6], [2]])
x = np.linalg.solve(m, n)
print(x)
# 输出:
# [[1.]
#  [2.]
#  [3.]]

# todo 7、计算矩阵的逆矩阵
"""
计算矩阵的逆矩阵
逆矩阵是一个矩阵，使得原矩阵和逆矩阵相乘得到单位矩阵。

对于一个 n x n 的方阵 A，如果存在一个矩阵 B 使得：
A * B = B * A = I
其中 I 是 n x n 的单位矩阵，那么矩阵 B 就是矩阵 A 的逆矩阵，记作 A^{-1}。

示例计算：
给定矩阵 a:
a = [[1, 2],
     [3, 4]]

步骤 1: 设逆矩阵
设矩阵 a 的逆矩阵为 b:
b = [[a, b],
     [c, d]]

步骤 2: 确定矩阵方程
根据逆矩阵的定义，矩阵 a 和矩阵 b 相乘应得到单位矩阵 I:
a * b = [[1, 2],      [[a, b],    [[1, 0],
          [3, 4]]  *   [c, d]]  =   [0, 1]]

展开矩阵乘法：
[[1*a + 2*c, 1*b + 2*d],
 [3*a + 4*c, 3*b + 4*d]] = [[1, 0],
                            [0, 1]]

步骤 3: 解方程组
我们得到以下方程组：
1. 1*a + 2*c = 1
2. 1*b + 2*d = 0
3. 3*a + 4*c = 0
4. 3*b + 4*d = 1

通过解这个方程组，我们可以得到：
a = -2, b = 1, c = 1.5, d = -0.5

因此，矩阵 a 的逆矩阵 b 为：
b = [[-2, 1],
     [1.5, -0.5]]

使用 NumPy 计算逆矩阵，代码如下：
"""
a = np.array([[1, 2], [3, 4]])
print(np.linalg.inv(a))
# 输出:
# [[-2.   1. ]
#  [ 1.5 -0.5]]
